function [weights, error, score, gameLength, xAxis] = ...
                qLearn(init_weights, lambda, gamma, eta, tau, max_moves, avgErrorCount, pieces)

piecesNum = length(pieces);
            
error = zeros(1, floor(max_moves / avgErrorCount));
xAxis = zeros(1, floor(max_moves / avgErrorCount));

score = zeros(1, max_moves);
gameLength = zeros(1, max_moves);

currentGame = 1;
currentErrorIndex = 1;

errorTmpIndex = 1;
errorTmp = 0;

totalScore = 0;
gameStart = 1;

weights = init_weights;
table = zeros(12, 6);

for moveCount = 1 : max_moves
    piece = pieces(randi(piecesNum));
    [inputs, tables, moveScores, scaledScores] = formInputs(table, piece);
    
    [quality, neuronOutputs, neuronInputs] = predictQuality(weights, ...
        inputs);
    
    action = getNextAction(quality, tau);
    
    newTable = tables{action};
    moveScore = moveScores(action);
    scaledScore = scaledScores(action);
    
    totalScore = totalScore + moveScore;
    
    nextMoveQuality = estimateNextMoveQuality(newTable, weights, pieces);
    correctOutput = (1 - gamma) * scaledScore + gamma * nextMoveQuality;
    
    gradients = calculateGradients(weights, neuronOutputs, ...
        neuronInputs, action, correctOutput, lambda);
    weights = calculateNewWeights(weights, gradients, eta);
    
    errorTmp = errorTmp + costFunction(quality(action), correctOutput, weights, ...
        lambda);
    if (errorTmpIndex >= avgErrorCount)
        error(currentErrorIndex) = errorTmp / avgErrorCount;
        xAxis(currentErrorIndex) = moveCount;
        
        disp([num2str(moveCount), ': ', num2str(error(currentErrorIndex))]);
        
        currentErrorIndex = currentErrorIndex + 1;
        
        errorTmp = 0;
        errorTmpIndex = 1;
    else
        errorTmpIndex = errorTmpIndex + 1;
    end
    
    if (moveScore == -800)
        score(currentGame) = totalScore;
        gameLength(currentGame) = moveCount - gameStart + 1;
        
        if (moveCount ~= max_moves)
            gameStart = moveCount + 1;
            currentGame = currentGame + 1;
        end
        
        totalScore = 0;
        table = zeros(12, 6);
    else
        table = newTable;
    end
end

score = score(1 : currentGame);
gameLength = gameLength(1 : currentGame);


function [ action ] = getNextAction( quality, tau )

quality = exp(quality / tau);
quality = quality / sum(quality);

action = find(cumsum(quality) > rand(1));
action = action(1);


